#!/usr/bin/python
# author: zdzhjx@gmail.com
# version: 0.1
import os
import shutil
import random
from hashlib import md5
import threading
import traceback
import bsddb
class FileDic():
    def __init__( self, tmdir='/tmp', name=None ):
        if not name:
            name = str(1000000*random.random())
        self.m_name = name
        self.m_dir = tmdir + '/' + self.m_name
        try:
            #ios.mkdir( self.m_dir )
            pass
        except:
            traceback.print_exc()

        self.m_dic = {}
        self.m_lock = threading.Lock()
        self.m_db = bsddb.hashopen( self.m_dir )

    def __savedata( self, k,data ):
        # save the real data to file
        try:
            self.m_dic[k] = self.__keymapfile( k )
            self.m_db[ self.m_dic[k] ] = data
        except:
            traceback.print_exc()
            return False
        return True

    def __readdata( self, k,key, remove=False ):
        # get real data from file
        data = ""
        try:
            data = self.m_db[ key ]
            if remove:
                del self.m_db[ key ]
        except:
            traceback.print_exc()
        return data
    
    def __keymapfile( self, k ):
        # get file full path
        f = md5(k).hexdigest()
        return f
        return self.m_dir + '/' + f[:2] + '/' + f[2:]

    def popitem( self ):
        # pop one element
        try:
            self.m_lock.acquire()
            k, key = self.m_dic.popitem()
            #

            data = self.__readdata( k,key, remove=True )
        except:
            traceback.print_exc()
            import pdb
            pdb.set_trace()
            k = None
            data = None
        finally:
            self.m_lock.release()

        return ( k, data )

    def push( self, k , v ):
        # push a element
        try:
            self.m_lock.acquire()
            self.__savedata( k, v )
        except:
            traceback.print_exc()
            return False
        finally:
            self.m_lock.release()
        return True
    
    def getele( self, k ):
        # return data by k
        try:
            self.m_lock.acquire()
            data = self.__readdata( k, self.m_dic[k] )
        except:
            traceback.print_exc()
        finally:
            self.m_lock.release()        
        return data
    
    def update( self, k , v ):
        # update v by k
        return self.push( k, v )

    def has_key( self, k ):
        # k in dic or not
        if k in self.m_dic:
            return True
        else:
            return False
    
    def bsize( self ):
        # return size of the FileDic
        return len(self.m_dic)
    
    # for compitable of dic
    def __setitem__( self, k, v ):
        self.push( k, v )
        return 
    
    def __getitem__( self, k ):
        data = self.getele( k )
        return data
    
    def __delitem__( self, k ):
        if self.has_key( k ):
            try:
                self.m_lock.acquire()
                #os.remove( self.__keymapfile(k) )
                del self.m_db[ self.m_dic[k] ]
                del self.m_dic[ k ]
            except:
                traceback.print_exc()
            finally:
                self.m_lock.release()

    def __len__( self ):
        return len( self.m_dic )

    def keys( self ):
        return self.m_dic.keys()

    def __del__( self ):
        try:
            self.m_db.close()
            #shutil.rmtree( self.m_dir )
        except:
            traceback.print_exc()

if __name__ == '__main__':
    # for test
    import pdb
    q = FileDic()
    print 'push a', q.push( 'a', '100' )
    #print 'push ab', q.push( 'ab', '100' )
    #pdb.set_trace()
    print 'del a', q.getele('a')
    print q.m_dic, q.m_db, 'KKKK'
    print 'popitem ',q.popitem()
    q['aa'] = '10000'
    print 'set aa'
    print 'aa', q['aa']
    del q['aa']
    print 'del aa'
    print 'dump keys', q.keys()
    q['ok'] = 'aaaaa'
    print q.keys()

